package com.fuxinglianbao.services;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.fuxinglianbao.config.AliyunSmsConfig;
import com.fuxinglianbao.entity.SmsRecord;
import com.fuxinglianbao.exception.BusinessException;
import com.fuxinglianbao.repository.SmsRecordRepository;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.security.SecureRandom;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;

@Service
public class SmsService {
    private static final Logger logger = LoggerFactory.getLogger(SmsService.class);
    private static final String SMS_CODE_PREFIX = "sms:code:";
    private static final String SMS_LIMIT_PREFIX = "sms:limit:";
    private static final String SMS_IP_LIMIT_PREFIX = "sms:ip:limit:";
    private static final int CODE_LENGTH = 6;
    private static final int CODE_EXPIRE_MINUTES = 5;
    private static final int LIMIT_TIMES = 5;
    private static final int IP_LIMIT_TIMES = 20;
    private static final int LIMIT_EXPIRE_HOURS = 24;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private AliyunSmsConfig smsConfig;

    @Autowired
    private SmsRecordRepository smsRecordRepository;

    @Autowired
    private HttpServletRequest request;

    private final IAcsClient acsClient;
    private final Gson gson;
    
    private final BloomFilter<String> ipBloomFilter;
    private final ThreadPoolExecutor smsThreadPool;

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 100;
    private static final long KEEP_ALIVE_TIME = 60L;

    @Autowired
    public SmsService(AliyunSmsConfig smsConfig) {
        this.gson = new Gson();
        DefaultProfile profile = DefaultProfile.getProfile(
            "cn-hangzhou",
            smsConfig.getAccessKeyId(),
            smsConfig.getAccessKeySecret());
        this.acsClient = new DefaultAcsClient(profile);
        
        // 初始化布隆过滤器
        this.ipBloomFilter = BloomFilter.create(
            Funnels.stringFunnel(StandardCharsets.UTF_8),
            10000, // 预计元素数量
            0.01   // 误判率
        );
        
        // 初始化线程池
        this.smsThreadPool = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(QUEUE_CAPACITY),
            new ThreadFactoryBuilder().setNameFormat("sms-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
        
        logger.info("SMS service initialized with Aliyun configuration and performance optimizations");
    }

    public void sendVerificationCode(String phone) {
        logger.info("Sending verification code to phone: {}", phone);
        // 验证手机号格式
        if (!isValidPhone(phone)) {
            throw new BusinessException("手机号格式不正确");
        }

        // 获取IP地址
        String ipAddress = getClientIpAddress();
        String ipLimitKey = SMS_IP_LIMIT_PREFIX + ipAddress;
        
        // 使用布隆过滤器快速检查IP
        if (ipBloomFilter.mightContain(ipAddress)) {
            // 只有当布隆过滤器显示可能存在时才检查Redis
            String ipLimitTimes = redisTemplate.opsForValue().get(ipLimitKey);
            int ipTimes = ipLimitTimes == null ? 0 : Integer.parseInt(ipLimitTimes);
            if (ipTimes >= IP_LIMIT_TIMES) {
                throw new BusinessException("当前IP发送次数超过限制，请24小时后再试");
            }
        }

        // 使用Redis管道批量获取限制信息
        String limitKey = SMS_LIMIT_PREFIX + phone;
        String codeKey = SMS_CODE_PREFIX + phone;
        List<String> keys = Arrays.asList(limitKey, ipLimitKey);
        List<String> limitValues = redisTemplate.opsForValue().multiGet(keys);
        
        int times = limitValues.get(0) == null ? 0 : Integer.parseInt(limitValues.get(0));
        int ipTimes = limitValues.get(1) == null ? 0 : Integer.parseInt(limitValues.get(1));
        
        if (times >= LIMIT_TIMES) {
            throw new BusinessException("发送次数超过限制，请24小时后再试");
        }

        // 生成验证码
        String code = generateVerificationCode();
        
        // 使用Redis管道批量更新
        redisTemplate.execute(new SessionCallback<List<Object>>() {
            @Override
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForValue().set(codeKey, code, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
                operations.opsForValue().set(limitKey, String.valueOf(times + 1), LIMIT_EXPIRE_HOURS, TimeUnit.HOURS);
                operations.opsForValue().set(ipLimitKey, String.valueOf(ipTimes + 1), LIMIT_EXPIRE_HOURS, TimeUnit.HOURS);
                return operations.exec();
            }
        });
        
        // 更新布隆过滤器
        ipBloomFilter.put(ipAddress);

        // 发送短信验证码
        try {
            // 发送短信验证码
            Map<String, String> templateParam = new HashMap<>();
            templateParam.put("code", code);
            sendSms(phone, smsConfig.getTemplates().getVerificationCode(), templateParam);
            logger.info("Successfully sent verification code to phone: {}", phone);
        } catch (BusinessException e) {
            logger.error("Failed to send verification code to phone: {}, error: {}", phone, e.getMessage());
            throw e;
        }
    }

    private static final int MAX_RETRY_TIMES = 3;
    private static final long INITIAL_RETRY_DELAY_MS = 1000;
    private static final long MAX_RETRY_DELAY_MS = 8000;
    private static final double BACKOFF_MULTIPLIER = 2.0;

    private void sendSms(String phone, String templateCode, Map<String, String> templateParam) {
        logger.info("Starting SMS sending process for phone: {}, template: {}", phone, templateCode);
        
        // 使用批量保存优化数据库操作
        List<SmsRecord> batchRecords = new ArrayList<>();
        SmsRecord smsRecord = new SmsRecord();
        smsRecord.setPhone(phone);
        smsRecord.setTemplateCode(templateCode);
        smsRecord.setTemplateParam(gson.toJson(templateParam));
        smsRecord.setSendStatus("PENDING");
        smsRecord.setIpAddress(getClientIpAddress());
        smsRecord.setCreateTime(LocalDateTime.now());
        batchRecords.add(smsRecord);
        
        // 使用JPA批量保存优化
        @Transactional
        public void saveBatchSmsRecords(List<SmsRecord> records) {
            int batchSize = 50;
            for (int i = 0; i < records.size(); i += batchSize) {
                List<SmsRecord> batch = records.subList(i, Math.min(records.size(), i + batchSize));
                smsRecordRepository.saveAll(batch);
            }
        }
        
        logger.debug("Created SMS records batch: {}", gson.toJson(batchRecords));
        
        // 使用线程池异步发送短信
        CompletableFuture.supplyAsync(() -> {
            int retryCount = 0;
            Exception lastException = null;
            
            // 使用信号量限制并发请求
            try (SemaphoreHolder holder = new SemaphoreHolder(10)) {
                while (retryCount < MAX_RETRY_TIMES) {
            try {
                SendSmsRequest request = new SendSmsRequest();
                request.setPhoneNumbers(phone);
                request.setSignName(smsConfig.getSignName());
                request.setTemplateCode(templateCode);
                request.setTemplateParam(gson.toJson(templateParam));

                logger.debug("Sending SMS request (attempt {}): {}", retryCount + 1, gson.toJson(request));
                SendSmsResponse response = acsClient.getAcsResponse(request);
                logger.debug("Received SMS response: {}", gson.toJson(response));

                if ("OK".equals(response.getCode())) {
                    smsRecord.setSendStatus("SUCCESS");
                    smsRecord.setBizId(response.getBizId());
                    smsRecord.setRetryCount(retryCount);
                    logger.info("Successfully sent SMS to {} after {} retries, bizId: {}", phone, retryCount, response.getBizId());
                    smsRecord.setUpdateTime(LocalDateTime.now());
                    smsRecordRepository.save(smsRecord);
                    return;
                } else {
                    String errorMessage = response.getMessage();
                    String errorCode = response.getCode();
                    
                    // 根据不同错误码进行分类处理
                    switch (errorCode) {
                        case "isv.MOBILE_NUMBER_ILLEGAL":
                            lastException = new BusinessException("手机号格式不正确");
                            break;
                        case "isv.BUSINESS_LIMIT_CONTROL":
                            lastException = new BusinessException("发送频率超限，请稍后再试");
                            break;
                        case "isv.OUT_OF_SERVICE":
                            lastException = new BusinessException("服务已停用");
                            break;
                        default:
                            lastException = new BusinessException("短信发送失败：" + errorMessage);
                    }
                    
                    smsRecord.setErrorCode(errorCode);
                    smsRecord.setErrorMessage(errorMessage);
                    logger.warn("Failed to send SMS to {} (attempt {}), error code: {}, message: {}", 
                        phone, retryCount + 1, errorCode, errorMessage);
            } catch (ClientException e) {
                lastException = e;
                String errorCode = e.getErrCode();
                String errorMsg = e.getErrMsg();
                smsRecord.setErrorCode(errorCode);
                smsRecord.setErrorMessage(errorMsg);
                logger.error("Client exception while sending SMS to {} (attempt {}): code={}, message={}", 
                    phone, retryCount + 1, errorCode, errorMsg);
                
                // 记录详细的异常堆栈信息
                logger.debug("Detailed exception for SMS sending failure:", e);
            }

            retryCount++;
            if (retryCount < MAX_RETRY_TIMES) {
                try {
                    // 计算指数退避延迟时间
                    long delayMs = (long) (INITIAL_RETRY_DELAY_MS * Math.pow(BACKOFF_MULTIPLIER, retryCount));
                    delayMs = Math.min(delayMs, MAX_RETRY_DELAY_MS);
                    // 添加随机抖动，避免多个请求同时重试
                    delayMs += new SecureRandom().nextInt((int)(delayMs * 0.1));
                    Thread.sleep(delayMs);
                    logger.debug("Waiting {} ms before retry attempt {}", delayMs, retryCount + 1);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }

        // All retries failed
        smsRecord.setSendStatus("FAILED");
        smsRecord.setRetryCount(retryCount);
        smsRecord.setUpdateTime(LocalDateTime.now());
        smsRecordRepository.save(smsRecord);

        logger.error("Failed to send SMS to {} after {} retries", phone, retryCount);
        if (lastException instanceof BusinessException) {
            throw (BusinessException) lastException;
        } else if (lastException instanceof ClientException) {
            throw new BusinessException("短信发送失败：" + lastException.getMessage());
        } else {
            throw new BusinessException("短信发送失败：未知错误");
        }
    }

    private static final int MAX_VERIFY_ATTEMPTS = 5;
    private static final String SMS_VERIFY_ATTEMPTS_PREFIX = "sms:verify:attempts:";

    public boolean verifyCode(String phone, String code) {
        if (code == null || code.length() != CODE_LENGTH) {
            logger.warn("Invalid verification code format for phone: {}", phone);
            return false;
        }
        
        // 使用Redis的原子操作优化验证过程
        String codeKey = SMS_CODE_PREFIX + phone;
        String attemptsKey = SMS_VERIFY_ATTEMPTS_PREFIX + phone;
        
        return redisTemplate.execute(new SessionCallback<Boolean>() {
            @Override
            public Boolean execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                
                String savedCode = operations.opsForValue().get(codeKey);
                if (savedCode == null) {
                    operations.discard();
                    return false;
                }
                
                if (savedCode.equals(code)) {
                    operations.delete(codeKey);
                    operations.delete(attemptsKey);
                    operations.exec();
                    return true;
                }
                
                // 增加验证尝试次数
                operations.opsForValue().increment(attemptsKey);
                operations.expire(attemptsKey, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
                operations.exec();
                return false;
            }
        });

        String codeKey = SMS_CODE_PREFIX + phone;
        String attemptsKey = SMS_VERIFY_ATTEMPTS_PREFIX + phone;

        // 获取验证尝试次数
        String attemptsStr = redisTemplate.opsForValue().get(attemptsKey);
        int attempts = attemptsStr != null ? Integer.parseInt(attemptsStr) : 0;

        if (attempts >= MAX_VERIFY_ATTEMPTS) {
            logger.warn("Max verification attempts reached for phone: {}", phone);
            throw new BusinessException("验证码尝试次数过多，请重新获取验证码");
        }

        String savedCode = redisTemplate.opsForValue().get(codeKey);
        if (savedCode == null) {
            logger.warn("Verification code not found or expired for phone: {}", phone);
            return false;
        }

        // 增加验证尝试次数
        redisTemplate.opsForValue().increment(attemptsKey);
        redisTemplate.expire(attemptsKey, CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        if (savedCode.equals(code)) {
            // 验证成功后删除验证码和尝试记录
            redisTemplate.delete(codeKey);
            redisTemplate.delete(attemptsKey);
            logger.info("Verification code successfully verified for phone: {}", phone);
            return true;
        }

        logger.warn("Invalid verification code for phone: {}, attempt: {}/{}", phone, attempts + 1, MAX_VERIFY_ATTEMPTS);
        return false;
    }

    private String generateVerificationCode() {
        // 使用ThreadLocal缓存SecureRandom实例
        ThreadLocal<SecureRandom> secureRandomThreadLocal = ThreadLocal.withInitial(() -> {
            byte[] seed = new byte[32];
            SecureRandom seedGenerator = new SecureRandom();
            seedGenerator.nextBytes(seed);
            return new SecureRandom(seed);
        });
        
        SecureRandom secureRandom = secureRandomThreadLocal.get();
        StringBuilder code = new StringBuilder(CODE_LENGTH);
        
        // 使用预计算的数字数组来提高性能
        char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        
        // 生成验证码，确保第一位不为0
        code.append(digits[1 + secureRandom.nextInt(9)]);
        for (int i = 1; i < CODE_LENGTH; i++) {
            code.append(digits[secureRandom.nextInt(10)]);
        }
        
        return code.toString();
    }

    private boolean isValidPhone(String phone) {
        // 使用预编译的正则表达式提高性能
        private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
        return phone != null && PHONE_PATTERN.matcher(phone).matches();
    }

    private String getClientIpAddress() {
        String clientIp = null;
        
        // 检查多种代理头，按优先级排序
        String[] headers = {
            "X-Real-IP",              // Nginx代理优先级最高
            "X-Forwarded-For",        // 标准代理头
            "Proxy-Client-IP",        // Apache代理
            "WL-Proxy-Client-IP",     // WebLogic代理
            "HTTP_X_FORWARDED_FOR",   // 其他代理
            "HTTP_CLIENT_IP",
            "HTTP_X_REAL_IP",
            "HTTP_X_FORWARDED",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR"             // 最后才使用远程地址
        };

        // 用于存储可能的代理链
        List<String> proxyChain = new ArrayList<>();

        for (String header : headers) {
            String value = request.getHeader(header);
            if (value != null && !value.isEmpty() && !"unknown".equalsIgnoreCase(value)) {
                // 处理代理链
                String[] ips = value.split(",");
                for (String ip : ips) {
                    ip = ip.trim();
                    if (isValidIpAddress(ip)) {
                        proxyChain.add(ip);
                    }
                }
                if (!proxyChain.isEmpty()) {
                    break;
                }
            }
        }

        // 如果有有效的代理链，使用第一个非内网IP
        if (!proxyChain.isEmpty()) {
            for (String ip : proxyChain) {
                if (!isInternalIp(ip)) {
                    clientIp = ip;
                    break;
                }
            }
            // 如果所有IP都是内网IP，使用第一个
            if (clientIp == null) {
                clientIp = proxyChain.get(0);
            }
        }

        // 如果没有从代理头获取到IP，使用远程地址
        if (clientIp == null) {
            clientIp = request.getRemoteAddr();
            if ("0:0:0:0:0:0:0:1".equals(clientIp)) {
                clientIp = "127.0.0.1";
            }
        }

        // 记录完整的代理链信息
        if (proxyChain.size() > 1) {
            logger.debug("Full proxy chain: {}", String.join(" -> ", proxyChain));
        }
        logger.debug("Resolved client IP: {}", clientIp);
        return clientIp;
    }

    private boolean isValidIpAddress(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return false;
        }
        
        // IPv4验证
        if (ip.matches("^(\\d{1,3}\\.){3}\\d{1,3}$")) {
            String[] parts = ip.split("\\.");
            for (String part : parts) {
                int value = Integer.parseInt(part);
                if (value < 0 || value > 255) {
                    return false;
                }
            }
            return true;
        }
        
        // IPv6验证
        return ip.matches("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$") ||
               ip.matches("^(([0-9a-fA-F]{1,4}:){0,6})?::([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}$");
    }

    private boolean isInternalIp(String ip) {
        if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
            return true;
        }
        
        try {
            InetAddress address = InetAddress.getByName(ip);
            
            // 检查是否是内网IP
            if (address.isSiteLocalAddress() || 
                address.isLinkLocalAddress() || 
                address.isLoopbackAddress()) {
                return true;
            }
            
            // 检查特定的内网IP段
            String[] parts = ip.split("\\.");
            if (parts.length == 4) {
                int firstOctet = Integer.parseInt(parts[0]);
                int secondOctet = Integer.parseInt(parts[1]);
                
                // 10.0.0.0/8
                if (firstOctet == 10) return true;
                
                // 172.16.0.0/12
                if (firstOctet == 172 && secondOctet >= 16 && secondOctet <= 31) return true;
                
                // 192.168.0.0/16
                if (firstOctet == 192 && secondOctet == 168) return true;
            }
            
            return false;
        } catch (UnknownHostException e) {
            logger.warn("Failed to parse IP address: {}", ip);
            return false;
        }
    }
}